// i.r(t),
//   i.d(t, {
//     MAX_RESOLUTION: () => C,
//     default: () => WebGLRendererModule
//   })

import * as THREE from "three"
import { GetScreenPositionCommand } from "../command/screen.command"
import { ShaderConfig } from "../constant/shader.const"
import CameraData from "../data/CameraData"
import CanvasData from "../data/CanvasData"
import SettingsData from "../data/SettingsData"
import EngineContext from "../EngineContext"
import cubeEnum from "../enum/cube.enum"
import { phaseState } from "../enum/phase.enum"
import { ContextLostException, RendererCreationException, WebGLSupportException } from "../exception/three.exception"
import M from "../math/27687"
import BlurCubeMesh from "../mesh/BlurCubeMesh"
import ScreenOverlayMesh from "../mesh/ScreenOverlayMesh"
import { PlayerResizedMessage } from "../message/play.message"
import { PixelRatioChangedMessage } from "../message/ratio.message"
import ee from "../shader/28737"
import a from "../shader/31090"
import ce from "../shader/55546"
import be from "../shader/64715"
import l from "../shader/69383"
import ae from "../shader/79826"
import fe from "../shader/93366"
import de from "../shader/96228"
import WebGLScene from "../three/WebGLScene"
import deviceUtil from "../utils/device.util"
import funcUtil from "../utils/func.util"
import Logger from "../utils/Logger"
import Module from "./Module"
const h = {
  basicTextured: {
    uniforms: {
      tDiffuse: { type: "t", value: null },
      alpha: { type: "f", value: 1 }
    },
    vertexShader: funcUtil.addNR(l, ae),
    fragmentShader: funcUtil.addNR(a, be)
  },
  screenSpaceColored: {
    uniforms: {
      color: { type: "v3", value: new THREE.Vector3(0, 0, 0) },
      opacity: { type: "f", value: 1 }
    },
    vertexShader: funcUtil.addNR(l, ce),
    fragmentShader: funcUtil.addNR(a, de)
  }
}
const m = (e, t) => {
  switch (e) {
    case cubeEnum.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
      t.set(0, -Math.PI / 2, 0)
      break
    case cubeEnum.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
      t.set(0, Math.PI / 2, 0)
      break
    case cubeEnum.GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
      t.set(Math.PI / 2, Math.PI, 0)
      break
    case cubeEnum.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
      t.set(-Math.PI / 2, Math.PI, 0)
      break
    case cubeEnum.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
      t.set(0, -Math.PI, 0)
      break
    case cubeEnum.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
      t.set(0, 0, 0)
  }
}
const g = (e: any, t: THREE.WebGLRenderer | THREE.WebGL1Renderer) => {
  const i = t.extensions
  const n = t.getContext()
  let s
  switch (e) {
    case THREE.RepeatWrapping:
      return n.REPEAT
    case THREE.ClampToEdgeWrapping:
      return n.CLAMP_TO_EDGE
    case THREE.MirroredRepeatWrapping:
      return n.MIRRORED_REPEAT
    case THREE.NearestFilter:
      return n.NEAREST
    case THREE.NearestMipMapNearestFilter:
      return n.NEAREST_MIPMAP_NEAREST
    case THREE.NearestMipMapLinearFilter:
      return n.NEAREST_MIPMAP_LINEAR
    case THREE.LinearFilter:
      return n.LINEAR
    case THREE.LinearMipMapNearestFilter:
      return n.LINEAR_MIPMAP_NEAREST
    case THREE.LinearMipMapLinearFilter:
      return n.LINEAR_MIPMAP_LINEAR
    case THREE.UnsignedByteType:
      return n.UNSIGNED_BYTE
    case THREE.UnsignedShort4444Type:
      return n.UNSIGNED_SHORT_4_4_4_4
    case THREE.UnsignedShort5551Type:
      return n.UNSIGNED_SHORT_5_5_5_1
    case THREE.UnsignedShort565Type:
      return n.UNSIGNED_SHORT_5_6_5
    case THREE.ByteType:
      return n.BYTE
    case THREE.ShortType:
      return n.SHORT
    case THREE.UnsignedShortType:
      return n.UNSIGNED_SHORT
    case THREE.IntType:
      return n.INT
    case THREE.UnsignedIntType:
      return n.UNSIGNED_INT
    case THREE.FloatType:
      return n.FLOAT
  }
  switch (e) {
    case THREE.AlphaFormat:
      return n.ALPHA
    case THREE.RGBFormat:
      return n.RGB
    case THREE.RGBAFormat:
      return n.RGBA
    case THREE.LuminanceFormat:
      return n.LUMINANCE
    case THREE.LuminanceAlphaFormat:
      return n.LUMINANCE_ALPHA
    case THREE.AddEquation:
      return n.FUNC_ADD
    case THREE.SubtractEquation:
      return n.FUNC_SUBTRACT
    case THREE.ReverseSubtractEquation:
      return n.FUNC_REVERSE_SUBTRACT
    case THREE.ZeroFactor:
      return n.ZERO
    case THREE.OneFactor:
      return n.ONE
    case THREE.SrcColorFactor:
      return n.SRC_COLOR
    case THREE.OneMinusSrcColorFactor:
      return n.ONE_MINUS_SRC_COLOR
    case THREE.SrcAlphaFactor:
      return n.SRC_ALPHA
    case THREE.OneMinusSrcAlphaFactor:
      return n.ONE_MINUS_SRC_ALPHA
    case THREE.DstAlphaFactor:
      return n.DST_ALPHA
    case THREE.OneMinusDstAlphaFactor:
      return n.ONE_MINUS_DST_ALPHA
    case THREE.DstColorFactor:
      return n.DST_COLOR
    case THREE.OneMinusDstColorFactor:
      return n.ONE_MINUS_DST_COLOR
    case THREE.SrcAlphaSaturateFactor:
      return n.SRC_ALPHA_SATURATE
  }
  if (e === THREE.HalfFloatType) {
    if (t.capabilities.isWebGL2) return n["HALF_FLOAT"]
    s = i.get("OES_texture_half_float")
    if (null !== s) return s.HALF_FLOAT_OES
  }
  s = i.get("WEBGL_compressed_texture_s3tc")
  if (null !== s) {
    if (e === THREE.RGB_S3TC_DXT1_Format) return s.COMPRESSED_RGB_S3TC_DXT1_EXT
    if (e === THREE.RGBA_S3TC_DXT1_Format) return s.COMPRESSED_RGBA_S3TC_DXT1_EXT
    if (e === THREE.RGBA_S3TC_DXT3_Format) return s.COMPRESSED_RGBA_S3TC_DXT3_EXT
    if (e === THREE.RGBA_S3TC_DXT5_Format) return s.COMPRESSED_RGBA_S3TC_DXT5_EXT
  }
  s = i.get("WEBGL_compressed_texture_pvrtc")
  if (null !== s) {
    if (e === THREE.RGB_PVRTC_4BPPV1_Format) return s.COMPRESSED_RGB_PVRTC_4BPPV1_IMG
    if (e === THREE.RGB_PVRTC_2BPPV1_Format) return s.COMPRESSED_RGB_PVRTC_2BPPV1_IMG
    if (e === THREE.RGBA_PVRTC_4BPPV1_Format) return s.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
    if (e === THREE.RGBA_PVRTC_2BPPV1_Format) return s.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
  }
  s = i.get("WEBGL_compressed_texture_etc1")
  if (null !== s && e === THREE.RGB_ETC1_Format) return s.COMPRESSED_RGB_ETC1_WEBGL
  s = i.get("EXT_blend_minmax")
  if (null !== s) {
    if (e === THREE.MinEquation) return s.MIN_EXT
    if (e === THREE.MaxEquation) return s.MAX_EXT
  }
  if (e === THREE.UnsignedInt248Type) {
    if (t.capabilities.isWebGL2) return n["UNSIGNED_INT_24_8"]
    s = i.get("WEBGL_depth_texture")
    if (null !== s) return s.UNSIGNED_INT_24_8_WEBGL
  }
  return 0
}
const f = {
  copyCubeMap: {
    uniforms: {
      tDiffuse: { type: "t", value: null },
      alpha: { type: "f", value: 1 }
    },
    vertexShader: funcUtil.addNR(l, ee),
    fragmentShader: funcUtil.addNR(a, fe)
  }
}
const y = {
  0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  1: [0, 0, 0, 0, 0, 0, 0, 3e-6, 229e-6, 0.005977, 0.060598, 0.24173, 0.382925, 0.24173, 0.060598, 0.005977, 229e-6, 3e-6, 0, 0, 0, 0, 0, 0, 0],
  2: [
    0, 0, 1e-6, 1e-5, 78e-6, 489e-6, 0.002403, 0.009245, 0.027835, 0.065591, 0.120978, 0.174666, 0.197413, 0.174666, 0.120978, 0.065591, 0.027835, 0.009245,
    0.002403, 489e-6, 78e-6, 1e-5, 1e-6, 0, 0
  ],
  3: [
    48e-6, 169e-6, 538e-6, 0.001532, 0.003907, 0.008921, 0.018247, 0.033432, 0.054867, 0.080658, 0.106212, 0.125283, 0.132372, 0.125283, 0.106212, 0.080658,
    0.054867, 0.033432, 0.018247, 0.008921, 0.003907, 0.001532, 538e-6, 169e-6, 48e-6
  ],
  4: [
    0.001133, 0.002316, 0.00445, 0.008033, 0.013627, 0.021724, 0.032542, 0.04581, 0.0606, 0.075333, 0.088001, 0.096603, 0.099654, 0.096603, 0.088001, 0.075333,
    0.0606, 0.04581, 0.032542, 0.021724, 0.013627, 0.008033, 0.00445, 0.002316, 0.001133
  ],
  5: [
    0.004571, 0.00723, 0.010989, 0.016048, 0.022521, 0.03037, 0.039354, 0.049003, 0.058632, 0.067411, 0.074476, 0.079066, 0.080657, 0.079066, 0.074476,
    0.067411, 0.058632, 0.049003, 0.039354, 0.03037, 0.022521, 0.016048, 0.010989, 0.00723, 0.004571
  ],
  6: [
    0.009379, 0.012899, 0.017256, 0.022453, 0.028417, 0.034983, 0.041888, 0.048784, 0.055264, 0.060893, 0.065262, 0.068032, 0.068981, 0.068032, 0.065262,
    0.060893, 0.055264, 0.048784, 0.041888, 0.034983, 0.028417, 0.022453, 0.017256, 0.012899, 0.009379
  ],
  7: [
    0.014185, 0.01793, 0.022207, 0.02695, 0.032045, 0.037335, 0.042622, 0.047676, 0.052254, 0.056116, 0.059048, 0.06088, 0.061504, 0.06088, 0.059048, 0.056116,
    0.052254, 0.047676, 0.042622, 0.037335, 0.032045, 0.02695, 0.022207, 0.01793, 0.014185
  ]
}
export class CWFWebGLRenderer {
  threeRenderer: THREE.WebGLRenderer | THREE.WebGL1Renderer
  cachedSize: THREE.Vector2
  renderToCubeMap: any
  copyCubemap: any
  blurCubemap: any
  _maxCubemapSize: number
  _maxTextureSize: number
  _gpuInfo: any
  constructor(e) {
    this.threeRenderer = e
    this.cachedSize = new THREE.Vector2()
    this.renderToCubeMap = (() => {
      let e,
        t,
        i,
        n,
        s,
        r = !1
      const a = 0.5
      return (l, c, d, u, p, m, g, f, v, y, b, S, E, T, w, _) => {
        r ||
          ((t = new THREE.OrthographicCamera(-0.5, a, a, -0.5, -200, 200)),
          (t.position.z = 150),
          (e = new THREE.Scene()),
          (e.name = "rttCube"),
          e.add(t),
          (i = new THREE.RawShaderMaterial({
            uniforms: {
              tDiffuse: { value: null },
              alpha: { value: 1 }
            },
            vertexShader: h.basicTextured.vertexShader,
            fragmentShader: h.basicTextured.fragmentShader,
            depthWrite: !1,
            depthTest: !1,
            side: THREE.DoubleSide
          })),
          (n = new THREE.PlaneBufferGeometry(1, 1)),
          (s = new THREE.Mesh(n, i)),
          (s.position.z = 0),
          e.add(s),
          (r = !0))
        const M = n.getAttribute("uv")
        M.setUsage(THREE.DynamicDrawUsage), (M.needsUpdate = !0)
        const x = M.array,
          A = p / d,
          O = m / u,
          D = g / d,
          C = f / u
        ;(x[0] = A), (x[1] = O + C), (x[2] = A + D), (x[3] = O + C), (x[4] = A), (x[5] = O), (x[6] = A + D), (x[7] = O)
        const P = n.getAttribute("position")
        P.setUsage(THREE.DynamicDrawUsage), (P.needsUpdate = !0)
        const I = P.array,
          N = v / c.width - a,
          L = y / c.height - a,
          R = b / c.width,
          k = S / c.height
        ;(I[0] = N),
          (I[1] = L + k),
          (I[3] = N + R),
          (I[4] = L + k),
          (I[6] = N),
          (I[7] = L),
          (I[9] = N + R),
          (I[10] = L),
          (i.uniforms.tDiffuse.value = l),
          (i.blending = T || THREE.NoBlending),
          (i.transparent = !!w),
          null == _ && (_ = 1),
          (i.uniforms.alpha.value = _),
          c.viewport.set(0, 0, c.width, c.height)
        const B = this.threeRenderer.getRenderTarget(),
          U = this.threeRenderer.autoClear,
          F = this.threeRenderer.xr.enabled
        ;(this.threeRenderer.xr.enabled = !1),
          (this.threeRenderer.autoClear = !1),
          this.threeRenderer.setRenderTarget(c, E),
          this.threeRenderer.render(e, t),
          this.threeRenderer.setRenderTarget(B),
          (this.threeRenderer.autoClear = U),
          (this.threeRenderer.xr.enabled = F)
      }
    })()
    this.copyCubemap = (() => {
      let e: THREE.Scene,
        t: THREE.OrthographicCamera,
        i: THREE.RawShaderMaterial,
        n: THREE.BoxGeometry,
        s: THREE.Mesh,
        r = !1
      const a = new THREE.Euler(),
        l = this
      return (c, h, d, u, p) => {
        if (!r) {
          const a = 2
          const l = a / 2
          t = new THREE.OrthographicCamera(-l, l, l, -l, 0, 200)
          t.position.set(0, 0, 0)
          e = new THREE.Scene()
          e.name = "copyCube"
          e.add(t)
          i = new THREE.RawShaderMaterial({
            uniforms: {
              tDiffuse: { value: null },
              alpha: { value: 1 }
            },
            vertexShader: f.copyCubeMap.vertexShader,
            fragmentShader: f.copyCubeMap.fragmentShader,
            depthWrite: !1,
            depthTest: !1,
            side: THREE.DoubleSide
          })
          n = new THREE.BoxGeometry(a, a, a)
          s = new THREE.Mesh(n, i)
          e.add(s)
          r = !0
        }
        const g = l.threeRenderer.getRenderTarget()
        const v = l.threeRenderer.autoClear
        const y = l.threeRenderer.xr.enabled
        l.threeRenderer.xr.enabled = !1
        l.threeRenderer.autoClear = !1
        for (let n = 0; n < 6; n++) {
          m(n, a)
          s.rotation.copy(a)
          s.matrixWorldNeedsUpdate = !0
          s.updateMatrixWorld(!0)
          i.uniforms.tDiffuse.value = c
          i.blending = d || THREE.NoBlending
          i.transparent = !!u
          null == p && (p = 1)
          i.uniforms.alpha.value = p
          h.viewport.set(0, 0, h.width, h.height)
          l.threeRenderer.setRenderTarget(h, n)
          l.threeRenderer.render(e, t)
        }
        l.threeRenderer.setRenderTarget(g)
        l.threeRenderer.autoClear = v
        l.threeRenderer.xr.enabled = y
      }
    })()
    this.blurCubemap = (() => {
      const e = e => {
        const t = new THREE.WebGLCubeRenderTarget(e)
        return (
          (t.texture.image = {}),
          (t.texture.image.width = e),
          (t.texture.image.height = e),
          {
            renderTarget: t,
            cubeCamera: new THREE.CubeCamera(0.1, 1e3, t)
          }
        )
      }
      const t = { 512: e(512) },
        i = new THREE.Scene(),
        n = new BlurCubeMesh(new THREE.BoxGeometry(1, 1, 1))
      i.add(n)
      const s = {}
      return (r, a, l = 3, c = 0.005) => {
        const h = a.renderTarget
        t[h.width] || (t[h.width] = e(h.width)),
          (n.material = (function (e) {
            if (!s[e]) {
              const t = new THREE.RawShaderMaterial({
                uniforms: THREE.UniformsUtils.clone(ShaderConfig.blurCube.uniforms),
                vertexShader: ShaderConfig.blurCube.vertexShader,
                fragmentShader: ShaderConfig.blurCube.fragmentShader,
                side: THREE.BackSide
              })
              t.defines = {}
              const i = y[`${e}`]
              for (const [e, n] of i.entries()) t.defines[`KERNEL_${e + 1}`] = 0 === n || 1 === n ? n.toFixed(1) : n
              s[e] = t
            }
            return s[e]
          })(l))
        const d = t[h.width]
        ;(n.material["uniforms"].map.value = r),
          n.material["uniforms"].dir.value.set(c, 0),
          d.cubeCamera.update(this.renderer, i),
          (n.material["uniforms"].map.value = d.renderTarget.texture),
          n.material["uniforms"].dir.value.set(0, c),
          a.update(this.renderer, i)
      }
    })()
    try {
      const e = this.threeRenderer.getContext()
      this._maxCubemapSize = e.getParameter(e.MAX_CUBE_MAP_TEXTURE_SIZE)
      this._maxTextureSize = e.getParameter(e.MAX_TEXTURE_SIZE)
      this._gpuInfo = deviceUtil.getGPU(e)
    } catch (e) {
      throw new RendererCreationException("Unable to create a WebGL rendering context")
    }
  }
  get renderer() {
    return this.threeRenderer
  }
  get maxCubemapSize() {
    return this._maxCubemapSize
  }
  get maxTextureSize() {
    return this._maxTextureSize
  }
  get gpuInfo() {
    return this._gpuInfo
  }
  getSize() {
    return this.threeRenderer.getSize(this.cachedSize)
  }
  getPixelRatio() {
    return this.threeRenderer.getPixelRatio()
  }
  dispose() {
    this.threeRenderer.dispose()
  }
  initSizedTexture2D(e: number, t: THREE.Texture) {
    const i = this.threeRenderer.getContext()
    const n = this.threeRenderer.state
    t = t || new THREE.Texture()
    const s = g(t.format, this.threeRenderer)
    const r = g(t.type, this.threeRenderer)
    const a = this.threeRenderer.properties.get(t)
    const l = i.createTexture()
    t.addEventListener("dispose", () => i.deleteTexture(a.__webglTexture))
    n.bindTexture(i.TEXTURE_2D, l)
    i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL, t.flipY)
    i.texImage2D(i.TEXTURE_2D, 0, s, e, e, 0, s, r, null)
    const c = g(t.wrapS, this.threeRenderer)
    const h = g(t.wrapT, this.threeRenderer)
    i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_S, c)
    i.texParameteri(i.TEXTURE_2D, i.TEXTURE_WRAP_T, h)
    i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MAG_FILTER, g(t.magFilter, this.threeRenderer))
    i.texParameteri(i.TEXTURE_2D, i.TEXTURE_MIN_FILTER, g(t.minFilter, this.threeRenderer))
    t.generateMipmaps && i.generateMipmap(i.TEXTURE_2D)
    n.bindTexture(i.TEXTURE_2D, null)
    a.__webglTexture = l
    return t
  }
  initSizedCubeMap(e: number, generateMipmaps: boolean) {
    const i = this.threeRenderer.getContext()
    const n = this.threeRenderer.state
    const s = new THREE.CubeTexture([null, null, null, null, null, null])
    s.flipY = !1
    s.generateMipmaps = generateMipmaps
    const r = g(s.format, this.threeRenderer)
    const a = g(s.type, this.threeRenderer)
    const l = this.threeRenderer.properties.get(s)
    const c = i.createTexture()
    n.bindTexture(i.TEXTURE_CUBE_MAP, c)
    i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL, s.flipY)
    for (let t = 0; t < 6; t++) i.texImage2D(i.TEXTURE_CUBE_MAP_POSITIVE_X + t, 0, r, e, e, 0, r, a, null)
    if (generateMipmaps) {
      s.magFilter = THREE.LinearFilter
      s.minFilter = THREE.LinearMipMapLinearFilter
      i.texParameteri(i.TEXTURE_CUBE_MAP, i.TEXTURE_MAG_FILTER, i.LINEAR)
      i.texParameteri(i.TEXTURE_CUBE_MAP, i.TEXTURE_MIN_FILTER, i.LINEAR_MIPMAP_LINEAR)
      i.generateMipmap(i.TEXTURE_CUBE_MAP)
    } else {
      s.magFilter = THREE.LinearFilter
      s.minFilter = THREE.LinearFilter
      i.texParameteri(i.TEXTURE_CUBE_MAP, i.TEXTURE_MAG_FILTER, i.LINEAR)
      i.texParameteri(i.TEXTURE_CUBE_MAP, i.TEXTURE_MIN_FILTER, i.LINEAR)
    }
    n.bindTexture(i.TEXTURE_CUBE_MAP, null)
    l.__image__webglTextureCube = c
    return s
  }
  initRenderTargetCube(e: number) {
    const t = new THREE.WebGLCubeRenderTarget(e, {
      stencilBuffer: !1,
      depthBuffer: !1,
      generateMipmaps: !1
    })
    t.texture.image = {}
    t.texture.image.width = e
    t.texture.image.height = e
    return t
  }
  deallocateCubeTexture(e) {
    const t = this.threeRenderer.getContext()
    const i = this.threeRenderer.properties.get(e)
    t.deleteTexture(i.__image__webglTextureCube)
  }
  uploadTexture2D(e, t, i, n) {
    const s = this.threeRenderer.getContext(),
      r = this.threeRenderer.state,
      o = s.getParameter(s.TEXTURE_BINDING_2D),
      a = this.threeRenderer.properties.get(t)
    r.bindTexture(s.TEXTURE_2D, a.__webglTexture),
      s.pixelStorei(s.UNPACK_FLIP_Y_WEBGL, t.flipY),
      s.pixelStorei(s.UNPACK_PREMULTIPLY_ALPHA_WEBGL, t.premultiplyAlpha),
      s.pixelStorei(s.UNPACK_ALIGNMENT, t.unpackAlignment),
      s.texParameteri(s.TEXTURE_2D, s.TEXTURE_WRAP_S, g(t.wrapS, this.threeRenderer)),
      s.texParameteri(s.TEXTURE_2D, s.TEXTURE_WRAP_T, g(t.wrapT, this.threeRenderer)),
      s.texParameteri(s.TEXTURE_2D, s.TEXTURE_MAG_FILTER, g(t.magFilter, this.threeRenderer)),
      s.texParameteri(s.TEXTURE_2D, s.TEXTURE_MIN_FILTER, g(t.minFilter, this.threeRenderer))
    const l = g(t.format, this.threeRenderer),
      c = g(t.type, this.threeRenderer)
    s.texSubImage2D(s.TEXTURE_2D, 0, i, n, l, c, e), t.generateMipmaps && s.generateMipmap(s.TEXTURE_2D), r.bindTexture(s.TEXTURE_2D, o)
  }
  generateMipmaps(e) {
    const t = this.threeRenderer.getContext(),
      i = this.threeRenderer.state,
      n = t.getParameter(t.TEXTURE_BINDING_2D),
      s = this.threeRenderer.properties.get(e)
    i.bindTexture(t.TEXTURE_2D, s.__webglTexture), e.generateMipmaps && t.generateMipmap(t.TEXTURE_2D), i.bindTexture(t.TEXTURE_2D, n)
  }
  fence(e) {
    return new Promise(function (t, i) {
      const n = e.fenceSync(e.SYNC_GPU_COMMANDS_COMPLETE, 0)
      if (null === n) return i()
      e.flush()
      const s = () => {
        switch (e.getSyncParameter(n, e.SYNC_STATUS)) {
          case e.TIMEOUT_EXPIRED:
            return void setTimeout(s, 0)
          case e.WAIT_FAILED:
            return void i()
          case e.SIGNALED:
            return e.deleteSync(n), void t(null)
          default:
            return void setTimeout(s, 0)
        }
      }
      setTimeout(s, 0)
    })
  }
}

class ScreenOverlayComponent {
  scene: any
  cameraData: any
  blackoutOverlayMesh: ScreenOverlayMesh
  constructor(e, t) {
    ;(this.scene = e), (this.cameraData = t)
  }
  init() {
    this.blackoutOverlayMesh = new ScreenOverlayMesh(0)
  }
  dispose() {
    this.blackoutOverlayMesh.dispose()
  }
  render(e) {
    this.blackoutOverlayMesh.opacity = this.cameraData.transition.blackoutProgress.value
  }
  activate(e) {
    this.scene.addChild(this.scene.ids.CameraRig, this.blackoutOverlayMesh)
  }
  deactivate(e) {
    this.scene.removeChild(this.scene.ids.CameraRig, this.blackoutOverlayMesh)
  }
}
class TextureTracker {
  stacks: Map<any, any>
  texInfo: Map<any, any>
  constructor() {
    ;(this.stacks = new Map()), (this.texInfo = new Map())
  }
  hookCalls(e) {
    let t = 1
    const i = e.createTexture.bind(e)
    e.createTexture = () => {
      const e = i()
      e.mpTexId = t++
      const n = new Error().stack
      if (n) {
        const t = this.stacks.get(n) || new Set()
        t.add(e.mpTexId), this.stacks.set(n, t)
      }
      return e
    }
    const n = e.deleteTexture.bind(e)
    e.deleteTexture = e => {
      if (null !== e) {
        const t = e.mpTexId
        this.stacks.forEach(e => e.delete(t)), this.texInfo.delete(t)
      }
      n(e)
    }
    const s = e.bindTexture.bind(e)
    let r
    e.bindTexture = (...e) => {
      const t = e[1]
      ;(r = null !== t ? t.mpTexId : null), s(...e)
    }
    const o = e.texImage2D.bind(e)
    e.texImage2D = (...e) => {
      if ((o(...e), r)) {
        const t = 4
        if (e.length >= 7)
          this.texInfo.set(r, {
            width: e[3],
            height: e[4],
            size: e[8] ? e[8].length : e[3] * e[4] * t
          })
        else {
          const i = e[5]
          i instanceof ImageData &&
            this.texInfo.set(r, {
              width: i.width,
              height: i.height,
              size: i.data.length
            }),
            (i instanceof HTMLImageElement || i instanceof HTMLVideoElement || i instanceof HTMLCanvasElement || i instanceof ImageBitmap) &&
              this.texInfo.set(r, {
                width: i.width,
                height: i.height,
                size: i.width * i.height * t
              })
        }
      }
    }
  }
  dumpTextureInfo() {
    console.log(this.texInfo)
    const e = Array.from(this.texInfo.values()).reduce((e, t) => e + t.size, 0) / 1024
    console.log(`Currently active textures: ${this.texInfo.size}, total allocated: ${e}k`)
    let t = 0
    this.stacks.forEach((e, i) => {
      const n = Array.from(e.values()),
        s = n.map(e => this.texInfo.get(e)).reduce((e, t) => (t ? e + t.size : e), 0) / 1024
      console.groupCollapsed(`Call site ${t}: ${n.length}, allocated: ${s}k`),
        n.forEach(e => {
          const t = this.texInfo.get(e)
          t && console.log(`width: ${t.width}, height: ${t.height}, size: ${t.size / 1024}k`)
        }),
        console.groupEnd(),
        t++
    })
  }
}

const D = new Logger("webgl-renderer")
const C = 3686400
export default class WebGLRendererModule extends Module {
  _cwfRenderer: CWFWebGLRenderer
  contextLostError: any
  setContextLostError: (e: any) => void
  getPixelRatioFor: (e: number, t: number) => number
  canvas: HTMLCanvasElement
  engine: EngineContext
  cameraData: CameraData
  settingsData: SettingsData
  scene: WebGLScene
  textureTracker: TextureTracker
  constructor() {
    super(...arguments)
    this.name = "webgl-renderer"
    this.contextLostError = null
    this.setContextLostError = e => {
      this.contextLostError = e
    }
    this.getPixelRatioFor = (e, t) => {
      const i = window.devicePixelRatio || 1
      return e * t * i <= C ? i : 1
    }
  }
  async init(e, t: EngineContext) {
    if (!("WebGLRenderingContext" in window)) throw new WebGLSupportException("WebGL not supported")
    let threeRenderer: THREE.WebGLRenderer | THREE.WebGL1Renderer
    this.canvas = e.canvas
    this.engine = t
    try {
      const t: THREE.WebGLRendererParameters = {
        canvas: this.canvas,
        antialias: e.antialias,
        alpha: !0,
        powerPreference: "high-performance"
      }
      threeRenderer = e.useWebGL2 ? new THREE.WebGLRenderer(t) : new THREE.WebGL1Renderer(t)
      threeRenderer.autoClear = !1
      threeRenderer.xr.enabled = !0
    } catch (e) {
      throw new RendererCreationException("Unable to create a WebGL rendering context")
    }
    this._cwfRenderer = new CWFWebGLRenderer(threeRenderer)
    ;[this.cameraData, this.settingsData] = await Promise.all([t.market.waitForData(CameraData), t.market.waitForData(SettingsData)])
    const Scene = new THREE.Scene()
    Scene.name = "main"
    window["scene"] = Scene
    this.scene = new WebGLScene(Scene, threeRenderer, this.cameraData, e.useEffectComposer)
    t.addComponent(this, this.scene)
    t.addComponent(this, new ScreenOverlayComponent(this.scene, this.cameraData))
    const a = await t.market.waitForData(CanvasData)
    this.setSize(a.width, a.height)
    this.bindings.push(
      t.subscribe(PlayerResizedMessage, e => this.setSize(e.width, e.height)),
      t.commandBinder.addBinding(GetScreenPositionCommand, async e => M.q9(this.cameraData, e.worldPosition).screenPosition)
    )
    if (e.trackTextureUsage)
      if (e.useWebGL2) D.error("Texture tracking for WebGL2 is not supported at the moment.")
      else {
        this.textureTracker = new TextureTracker()
        this.textureTracker.hookCalls(threeRenderer.getContext())
      }
    this.canvas.addEventListener("webglcontextlost", this.setContextLostError)
  }
  dispose(e) {
    super.dispose(e)
    this.canvas.removeEventListener("webglcontextlost", this.setContextLostError)
  }
  onUpdate(e) {
    if (this.contextLostError) throw new ContextLostException(`WebGL context lost: ${this.contextLostError.statusMessage || "no status given"}`)
  }
  get gpuInfo() {
    return this._cwfRenderer.gpuInfo
  }
  get maxCubemapSize() {
    return this._cwfRenderer.maxCubemapSize
  }
  get maxTextureSize() {
    return this._cwfRenderer.maxTextureSize
  }
  getScene() {
    return this.scene
  }
  getCamera() {
    return this.scene.camera
  }
  get cwfRenderer() {
    return this._cwfRenderer
  }
  get textureManager() {
    return this._cwfRenderer
  }
  get threeRenderer() {
    return this.cwfRenderer.renderer
  }
  async renderOnce() {
    this.scene.startRender(!0)
    return this.engine.after(phaseState.Render).then(() => this.scene.startRender(!1))
  }
  startRender(e = !0) {
    this.scene.startRender(e)
    this.engine.broadcast(new PixelRatioChangedMessage(this.cwfRenderer.getPixelRatio()))
  }
  setSize(e: number, t: number) {
    const i = this.getPixelRatioFor(e, t)
    i !== this.cwfRenderer.getPixelRatio() && (D.debug(`Setting pixel ratio to ${i}`), this.engine.broadcast(new PixelRatioChangedMessage(i)))
    this.threeRenderer.xr.isPresenting && D.error("setting size while in xr is a no no, it resets the viewport", e, t)
    this.threeRenderer.setPixelRatio(i)
    this.threeRenderer.setSize(e, t, !1)
    this.scene.setCameraDirty()
  }
  supportsInstancing() {
    const { threeRenderer, settingsData } = this
    return !settingsData.checkUrlParam("noInstancing", 1, 0) && (threeRenderer.capabilities.isWebGL2 || threeRenderer.extensions.has("ANGLE_instanced_arrays"))
  }
  getCapabilities() {
    const { capabilities: e, extensions: t } = this.threeRenderer
    const i = e.isWebGL2
    let n = !1
    try {
      n = !!document.createElement("canvas").getContext("webgl2")
    } catch (e) {}
    return {
      gl_v2: n,
      gl_max_textures: e.maxTextures,
      gl_max_texture_size: e.maxTextureSize,
      gl_max_cubemap_size: e.maxCubemapSize,
      gl_instancing: this.supportsInstancing(),
      gl_oes_texture_float: i || t.has("OES_texture_float"),
      gl_oes_texture_half_float: i || t.has("OES_texture_half_float"),
      gl_depth_texture: i || t.has("WEBGL_depth_texture"),
      gl_draw_buffers: i || t.has("WEBGL_draw_buffers"),
      gl_oes_fbo_render_mip_map: i || t.has("OES_fbo_render_mipmap"),
      gl_shader_texture_lod: i || t.has("EXT_shader_texture_lod"),
      gl_oes_vertex_array_obj: i || t.has("OES_vertex_array_object"),
      gl_ovr_multi_view: i && t.has("OVR_multiview2"),
      gl_color_buffer_float: i && t.has("EXT_color_buffer_float"),
      gl_astc_supported: t.has("WEBGL_compressed_texture_astc"),
      gl_etc1_supported: t.has("WEBGL_compressed_texture_etc1"),
      gl_etc2_supported: t.has("WEBGL_compressed_texture_etc"),
      gl_dxt_supported: t.has("WEBGL_compressed_texture_s3tc"),
      gl_bptc_supported: t.has("EXT_texture_compression_bptc"),
      gl_pvrtc_supported: t.has("WEBGL_compressed_texture_pvrtc") || t.has("WEBKIT_WEBGL_compressed_texture_pvrtc")
    }
  }
}
